package ar.edu.unq.model;

import java.util.HashMap;
import java.util.Map;

import ar.edu.unq.exceptions.EquipoIncompletoException;

/**
 * Clase abstracta que representa una estrategia para la formacion del equipo.
 */
public abstract class FormacionStrategy {

    private int id;

    private Map<Posicion, Integer> posicionesCantidad = new HashMap<Posicion, Integer>();

    public FormacionStrategy() {
        this.crearListaPosiciones();
    }

    protected abstract void crearListaPosiciones();

    protected abstract Jugador getMejor(Posicion posicion, Equipo equipo, Formacion formacion);

    /**
     * Se encarga de armar la mejor formación posible. En el caso de que el equipo contenga menos de 11 jugadores,
     * dispara una excepcion de negocio
     * 
     * @throws EquipoIncompletoException
     *             En el caso de que el equipo tenga menos de 11 Jugadores
     * 
     * @author Nicolas Faure
     */
    public Formacion armarFormacion(final Equipo equipo) {
        if (equipo.getJugadores().size() < 11) {
            throw new EquipoIncompletoException(
                    "El equipo no está completo!, por favor verifique que haya 11 jugadores");
        }
        Formacion formacion = new Formacion();
        this.agregarMejorArquero(equipo, formacion);
        this.agregarMejoresCentrales(equipo, formacion);
        this.agregarMejoresLaterales(equipo, formacion);
        this.agregarMejoresVolantesDefensivos(equipo, formacion);
        this.agregarMejoresVolantesLaterales(equipo, formacion);
        this.agregarMejoresEnganches(equipo, formacion);
        this.agregarMejoresMediaPuntas(equipo, formacion);
        this.agregarMejoresDelanteros(equipo, formacion);
        return formacion;
    }

    /**
     * Agrega un Jugador a la formacion en la posicion indicada
     * 
     * @author Nicolas Faure
     */
    protected void agregarMejorJugador(final Equipo equipo, final Formacion formacion, final Posicion posicion) {
        for (int i = 0; i < this.getPosiciones().get(posicion); i++) {
            Jugador mejorJugador = this.getMejor(posicion, equipo, formacion);
            formacion.agregarTitular(new Titular(posicion, mejorJugador));
        }

    }

    /**
     * Se encarga de agregar a la formacion el mejor Arquero disponible en el equipo.
     * 
     * @author Nicolas Faure
     */
    protected void agregarMejorArquero(final Equipo equipo, final Formacion formacion) {
        this.agregarMejorJugador(equipo, formacion, Posicion.Arquero);
    }

    /**
     * Se encarga de agregar a la formacion los mejores Centrales disponibles en el equipo.
     * 
     * @author Nicolas Faure
     */
    protected void agregarMejoresCentrales(final Equipo equipo, final Formacion formacion) {
        this.agregarMejorJugador(equipo, formacion, Posicion.Central);
    }

    /**
     * Se encarga de agregar a la formacion los mejores Centrales disponibles en el equipo.
     * 
     * @author Nicolas Faure
     */
    protected void agregarMejoresLaterales(final Equipo equipo, final Formacion formacion) {
        this.agregarMejorJugador(equipo, formacion, Posicion.Lateral);
    }

    /**
     * Se encarga de agregar a la formacion los mejores Centrales disponibles en el equipo.
     * 
     * @author Nicolas Faure
     */
    protected void agregarMejoresVolantesDefensivos(final Equipo equipo, final Formacion formacion) {
        this.agregarMejorJugador(equipo, formacion, Posicion.VolanteDefensivo);
    }

    /**
     * Se encarga de agregar a la formacion los mejores Volantes Laterales disponibles en el equipo.
     * 
     * @author Nicolas Faure
     */
    protected void agregarMejoresVolantesLaterales(final Equipo equipo, final Formacion formacion) {
        this.agregarMejorJugador(equipo, formacion, Posicion.VolanteLateral);
    }

    /**
     * Se encarga de agregar a la formacion los mejores Enganches disponibles en el equipo.
     * 
     * @author Nicolas Faure
     */
    protected void agregarMejoresEnganches(final Equipo equipo, final Formacion formacion) {
        this.agregarMejorJugador(equipo, formacion, Posicion.Enganche);
    }

    /**
     * Se encarga de agregar a la formacion los mejores Medias Puntas disponibles en el equipo.
     * 
     * @author Nicolas Faure
     */
    protected void agregarMejoresMediaPuntas(final Equipo equipo, final Formacion formacion) {
        this.agregarMejorJugador(equipo, formacion, Posicion.MediaPunta);
    }

    /**
     * Se encarga de agregar a la formacion los mejores Delanteros disponibles en el equipo.
     * 
     * @author Nicolas Faure
     */
    protected void agregarMejoresDelanteros(final Equipo equipo, final Formacion formacion) {
        this.agregarMejorJugador(equipo, formacion, Posicion.Delantero);
    }

    protected void addArquero() {
        this.addJugador(Posicion.Arquero, 1);
    }

    /**
     * Se encarga de agregar los defensores centrales
     * 
     * @author Nicolas Faure
     * @param int Cantidad de Defensores a Agregar
     */
    protected void addCentrales(final int cantidadCentrales) {
        this.addJugador(Posicion.Central, cantidadCentrales);
    }

    /**
     * Se encarga de agregar los defensores centrales
     * 
     * @author Nicolas Faure
     * @param int Cantidad de Defensores a Agregar
     */
    protected void addLaterales(final int cantidadLaterales) {
        this.addJugador(Posicion.Lateral, cantidadLaterales);
    }

    /**
     * Se encarga de agregar los Volantes Defensivos
     * 
     * @author Nicolas Faure
     * @param int Cantidad de Volantes Defensivos a Agregar
     */
    protected void addVolantesDefensivos(final int cantidadVolantesDefensivos) {
        this.addJugador(Posicion.VolanteDefensivo, cantidadVolantesDefensivos);
    }

    /**
     * Se encarga de agregar los Volantes Laterales
     * 
     * @author Nicolas Faure
     * @param int Cantidad de Volantes Laterales
     */
    protected void addVolantesLaterales(final int cantidadVolantesLaterales) {
        this.addJugador(Posicion.VolanteLateral, cantidadVolantesLaterales);
    }

    /**
     * Se encarga de agregar los Enganches
     * 
     * @author Nicolas Faure
     * @param int Cantidad de Enganches
     */
    protected void addEnganches(final int cantidadEnganches) {
        this.addJugador(Posicion.Enganche, cantidadEnganches);
    }

    /**
     * Se encarga de agregar los Media Puntas
     * 
     * @author Nicolas Faure
     * @param int Cantidad de Media Puntas
     */
    protected void addMediaPuntas(final int cantidadMediaPuntas) {
        this.addJugador(Posicion.MediaPunta, cantidadMediaPuntas);
    }

    /**
     * Se encarga de agregar los Delanteros
     * 
     * @author Nicolas Faure
     * @param int Cantidad de Delanteros
     */
    protected void addDelanteros(final int cantidadDelanteros) {
        this.addJugador(Posicion.Delantero, cantidadDelanteros);
    }

    /**
     * Se encarga de agregar Jugadores a la formación en una posición específica
     */
    private void addJugador(final Posicion posicion, final int cantidad) {
        this.getPosiciones().put(posicion, cantidad);

    }

    // Getters & Setters

    public void setPosiciones(final Map<Posicion, Integer> posiciones) {
        this.posicionesCantidad = posiciones;
    }

    public Map<Posicion, Integer> getPosiciones() {
        return this.posicionesCantidad;
    }

    public int getId() {
        return this.id;
    }

    public void setId(final int id) {
        this.id = id;
    }
}
